/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package CellWorld;

import MDP.MDP;
import MDP.MDPPerception;
import MDP.MDPRewardFunction;
import MDP.MDPSource;
import MDP.MDPTransitionModel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.List;
import java.util.Random;
import utiles.Randomizer;
import utiles.datastructure.Pair;
import clases.Tablero;
/**
 * @author Ravi Mohan
 * 
 * esta clase va agregando celdas con el valor de recompensa de cada celda al laberinto
 *
 */
public class CellWorld implements MDPSource<CellWorldPosition, String> {
        public static final String LEFT = "left";

        public static final String RIGHT = "right";

        public static final String UP = "up";

        public static final String DOWN = "down";

        public static final String NO_OP = "no_op";

        List<Cell> blockedCells, allCells;
        
        List<Cell> ocupadaCells;
        
        private int numberOfRows;

        private int numberOfColumns;

        private List<Cell> terminalStates;

        private Cell initialState;
        
        private CellWorldPosition startingPosition;
        
        private List<Cell> celdaFinal;
        
        private Cell celdaCreada;
        
        
        
    
    

        public CellWorld(int numberOfRows, int numberOfColumns, double initialReward) {
                allCells = new ArrayList<Cell>();
                blockedCells = new ArrayList<Cell>();

                terminalStates = new ArrayList<Cell>();

                this.numberOfRows = numberOfRows;
                this.numberOfColumns = numberOfColumns;

                for (int row = 1; row <= numberOfRows; row++) {
                        for (int col = 1; col <= numberOfColumns; col++) {
                                allCells.add(new Cell(row, col, initialReward));
                        }
                }

                initialState = getCellAt(1, 1);
        }

        public void markBlocked(int i, int j) {
                blockedCells.add(getCellAt(i, j));

        }
        
        public void markOcupada(int i, int j) {
                ocupadaCells.add(getCellAt(i, j));

        }
       // método creado por mi
        public Cell getCelda(int i, int j){
            celdaCreada = getCellAt(i, j);
            return celdaCreada;
        }
        
          public List<Cell> getCeldaFinal(){
            celdaFinal = blockedCells;
            return celdaFinal;
        }
        
        //las casillas bloqueadas son las casillas visitadas
        //las casillas ocupadas son las que tiene promios o castigos
        private boolean isBlocked(int i, int j) {
                if ((i < 1) || (i > numberOfRows) || (j < 1) || (j > numberOfColumns)) {
                        return true;
                }
                for (Cell c : blockedCells) {
                        if ((c.getX() == i) && (c.getY() == j)) {
                                return true;
                        }
                }
                return false;
        }
        
        private boolean isOcupada(int i, int j) {
                if ((i < 1) || (i > numberOfRows) || (j < 1) || (j > numberOfColumns)) {
                        return true;
                }
                for (Cell c : ocupadaCells) {
                        if ((c.getX() == i) && (c.getY() == j)) {
                                return true;
                        }
                }
                return false;
        }

        private Cell getCellAt(int i, int j) {
                for (Cell c : allCells) {
                        if ((c.getY() == i) && (c.getX() == j)) {
                                return c;
                        }
                                
                }
                  

                throw new RuntimeException("No se encontró Celda en la posición " + i + " , " + j);
        }

//mover probabilisticamente desde
        public CellWorldPosition moveProbabilisticallyFrom(int i, int j,
                        String direction, Randomizer r) {
                Cell c = getCellAt(i, j);
                if (terminalStates.contains(c)) {//pregunta si el elemento c ya esta dentro de la colección de terminalStates
                        return c.position(); //si se encuentra, entonces devuelve su posición
                }
                return moveFrom(i, j, determineDirectionOfActualMovement(direction, r));

        }

        private CellWorldPosition moveFrom(int i, int j, String direction) {
                if (direction.equals(LEFT)) {
                        return moveLeftFrom(i, j);
                }
                if (direction.equals(RIGHT)) {
                        return moveRightFrom(i, j);
                }
                if (direction.equals(UP)) {
                        return moveUpFrom(i, j);
                }
                if (direction.equals(DOWN)) {
                        return moveDownFrom(i, j);
                }
                throw new RuntimeException("Imposible mover hacia " + direction + " desde " + i
                                + " , " + j);
        }

        private CellWorldPosition moveFrom(CellWorldPosition startingPosition,
                        String direction) {
                return moveFrom(startingPosition.getX(), startingPosition.getY(),
                                direction);
        }
        
        
        private String determineDirectionOfActualMovement(
                        String commandedDirection, double prob) {
                if (prob <= 0.7) {
                        return commandedDirection;
                } else if ((prob > 0.7) && (prob < 0.85)) {
                        if ((commandedDirection.equals(LEFT))
                                        || (commandedDirection.equals(RIGHT))) {
                            
                            System.out.println("Acción realizada: ARRIBA");
		            return UP;
                        }
                        if ((commandedDirection.equals(UP))
                                        || (commandedDirection.equals(DOWN))) {
                            System.out.println("Acción realizada: IZQUIERDA");
		            return LEFT;
                        }
                } else { // 0.9 < prob < 1.0
                        if ((commandedDirection.equals(LEFT))
                                        || (commandedDirection.equals(RIGHT))) {
                            System.out.println("Acción realizada: ABAJO");
		            return DOWN;
                        }
                        if ((commandedDirection.equals(UP))
                                        || (commandedDirection.equals(DOWN))) {
                            System.out.println("Acción realizada: DERECHA");
		            return RIGHT;
                        }
                }
                throw new RuntimeException(
                                "Impsible determinar la dirección cuando el comando =  "
                                                + commandedDirection + " y la probabilidad = " + prob);

        }

        public String determineDirectionOfActualMovement(String commandedDirection, Randomizer r) {
                return determineDirectionOfActualMovement(commandedDirection,
                                r.nextDouble());

        }

         private CellWorldPosition moveLeftFrom(int i, int j) {
                if (isBlocked(j, i - 1)) {
                        return new CellWorldPosition(j, i);
                }
                return new CellWorldPosition(j, i - 1);
        }

        private CellWorldPosition moveUpFrom(int i, int j) {
                if (isBlocked(j + 1, i)) {
                        return new CellWorldPosition(j, i);
                }
                return new CellWorldPosition(j + 1, i);
        }

        private CellWorldPosition moveRightFrom(int i, int j) {
                if (isBlocked(j, i + 1)) {
                        return new CellWorldPosition(j, i);
                }
                return new CellWorldPosition(j, i + 1);
        }

        private CellWorldPosition moveDownFrom(int i, int j) {
                if (isBlocked(j - 1, i)) {
                        return new CellWorldPosition(j, i);
                }
                return new CellWorldPosition(j - 1, i);
        }
     /*   private CellWorldPosition moveLeftFrom(int i, int j) {
                if (isBlocked(i, j - 1)) {
                        return new CellWorldPosition(i, j);
                }
                return new CellWorldPosition(i, j - 1);
        }

        private CellWorldPosition moveRightFrom(int i, int j) {
                if (isBlocked(i, j + 1)) {
                        return new CellWorldPosition(i, j);
                }
                return new CellWorldPosition(i, j + 1);
        }

        private CellWorldPosition moveUpFrom(int i, int j) {
                if (isBlocked(i + 1, j)) {
                        return new CellWorldPosition(i, j);
                }
                return new CellWorldPosition(i + 1, j);
        }

        private CellWorldPosition moveDownFrom(int i, int j) {
                if (isBlocked(i - 1, j)) {
                        return new CellWorldPosition(i, j);
                }
                return new CellWorldPosition(i - 1, j);
        }*/

        public void setReward(int i, int j, double reward) {
                Cell c = getCellAt(i, j);
                c.setReward(reward);

        }

        public List<Cell> unblockedCells() {
                List<Cell> res = new ArrayList<Cell>();
                for (Cell c : allCells) {
                        if (!(blockedCells.contains(c))) {
                                res.add(c);
                        }
                }
                return res;
        }

        public boolean isBlocked(Pair<Integer, Integer> p) {
                return isBlocked(p.getFirst(), p.getSecond());
        }

        // ¿Cuál es la probabilidad de que a partir de la adopción de posiciones p1 y una acción
        // reaaching método posición p2 es pública sólo para las pruebas no se utilizan en el código de cliente.
        
        
        
        public double getTransitionProbability(CellWorldPosition startingPosition,String actionDesired, CellWorldPosition endingPosition) {

                String firstRightAngledAction = determineDirectionOfActualMovement(actionDesired, 0.80); //primera accion en ángulo recto, son las acciones a realizar en caso de que no se cumpla la probabilidad de la accion original
                String secondRightAngledAction = determineDirectionOfActualMovement(actionDesired, 0.95);//segunda accion en ángulo recto, 

                Hashtable<String, CellWorldPosition> actionsToPositions = new Hashtable<String, CellWorldPosition>(); //acciones para las posiciones
                actionsToPositions.put(actionDesired,moveFrom(startingPosition, actionDesired));
                actionsToPositions.put(firstRightAngledAction,moveFrom(startingPosition, firstRightAngledAction));
                actionsToPositions.put(secondRightAngledAction,moveFrom(startingPosition, secondRightAngledAction));

                Hashtable<CellWorldPosition, Double> positionsToProbability = new Hashtable<CellWorldPosition, Double>();//probabilidad de las posiciones
                for (CellWorldPosition p : actionsToPositions.values()) {
                        positionsToProbability.put(p, 0.0);
                }
//verifica que el estado al que llego sea el que me proporciona la mayor probabilidad, si es así , ese estado le setea la probabilidad 0.7, sino le setea 0.15
                Double valor = 0.0;
                for (String action : actionsToPositions.keySet()) {
                        CellWorldPosition position = actionsToPositions.get(action);
                        double value = positionsToProbability.get(position);
                        if (action.equals(actionDesired)) {
                                positionsToProbability.put(position, 0.7);
                                
                        } else if(valor == 0.0){ // pasos en ángulo recto
                                positionsToProbability.put(position, 0.84);
                                valor = 1.0;
                        }else
                            positionsToProbability.put(position, 0.95);

                }

                if (positionsToProbability.keySet().contains(endingPosition)) {
                        return positionsToProbability.get(endingPosition);
                } else {
                        return 0.0;
                }

        }

        public MDPTransitionModel<CellWorldPosition, String> getTransitionModel() {
                List<CellWorldPosition> terminalPositions = new ArrayList<CellWorldPosition>();// son los s' del T(s, a, s')
                for (Cell tc : terminalStates) {
                        terminalPositions.add(tc.position());
                }
                MDPTransitionModel<CellWorldPosition, String> mtm = new MDPTransitionModel<CellWorldPosition, String>(
                                terminalPositions);

                List<String> actions = Arrays.asList(new String[] { UP, DOWN, LEFT,
                                RIGHT });
   
                
               for (CellWorldPosition startingPosition : getNonFinalStates()) {
                //startingPosition = getStartStates();
                        for (String actionDesired : actions) {
                                for (CellWorldPosition target : getNonFinalStates()) { // demasiado trabajo? sólo debe
                                                                      //pasar por las céldas vecinas en lugar de todas las céldas.
                                        CellWorldPosition endingPosition = target;
                                        double transitionProbability = getTransitionProbability(startingPosition, actionDesired, endingPosition);
                                        if (!(transitionProbability == 0.0)) {

                                                mtm.setTransitionProbability(startingPosition,
                                                                actionDesired, endingPosition,
                                                                transitionProbability);
                                                
                                        }
                                }
                        }
                }
                return mtm;
        }
        

    
        public MDPRewardFunction<CellWorldPosition> getRewardFunction() {

                MDPRewardFunction<CellWorldPosition> result = new MDPRewardFunction<CellWorldPosition>();
                for (Cell c : unblockedCells()) {
                        CellWorldPosition pos = c.position();
                        double reward = c.getReward();
                        result.setReward(pos, reward);
                }

                return result;
        }

        public List<CellWorldPosition> unblockedPositions() {
                List<CellWorldPosition> result = new ArrayList<CellWorldPosition>();
                for (Cell c : unblockedCells()) {
                        result.add(c.position());
                }
                return result;
        }

        public MDP<CellWorldPosition, String> asMdp() {

                return new MDP<CellWorldPosition, String>((MDPSource<CellWorldPosition, String>) this);
        }

        public List<CellWorldPosition> getNonFinalStates() {// devolver los estados que no son finales
                List<CellWorldPosition> nonFinalPositions = unblockedPositions();
                nonFinalPositions.remove(getCeldaFinal());
//                nonFinalPositions.remove(getCellAt(3, 4).position());
                return nonFinalPositions;
        }
        
        

        public CellWorldPosition getStartStates() {// devolver el estado inicial de la funcion de transicion
                CellWorldPosition startingPositions = getInitialState();
                return startingPositions;
        }
        
       public List<CellWorldPosition> getFinalStates() { //devolver el estado final
                
            //genera el estado final de corona en forma aleatoria
                /*int i = (int) Math.floor(Math.random()*(6-2+1)+2);  // Valor entre M y N, ambos incluidos.
                int j = (int) Math.floor(Math.random()*(8-1+1)+1);  // Valor entre M y N, ambos incluidos.
                
                //genera el estado final de pozo en forma aleatoria    
                int i1 = (int) Math.floor(Math.random()*(6-2+1)+2);  // Valor entre M y N, ambos incluidos.;
                int j1 = (int) Math.floor(Math.random()*(8-1+1)+1);  // Valor entre M y N, ambos incluidos.
                while (i1 == i || j1 == j) {
                     i1 = (int) Math.floor(Math.random()*(6-2+1)+2);  // Valor entre M y N, ambos incluidos.
                     j1 = (int) Math.floor(Math.random()*(8-1+1)+1);  // Valor entre M y N, ambos incluidos.
                }
                
                List<CellWorldPosition> finalPositions = new ArrayList<CellWorldPosition>();
                finalPositions.add(getCellAt(i, j).position());
                finalPositions.add(getCellAt(i1, j1).position());
                return finalPositions;*/
           
           int i = (int) Math.floor(Math.random()*(6-2+1)+2);  // Valor entre M y N, ambos incluidos.
                int j = (int) Math.floor(Math.random()*(8-1+1)+1);  // Valor entre M y N, ambos incluidos.
                List<CellWorldPosition> finalPositions = new ArrayList<CellWorldPosition>();
                finalPositions.add(getCellAt(i, j).position());
                return finalPositions;
        }
       
           
      public CellWorldPosition getEstadoFinal() { //devolver el estado final
            
          return null;
        }
        

      
   
    
        
        

        public void setTerminalState(int i, int j) {
                setTerminalState(new CellWorldPosition(i, j));

        }

        public void setTerminalState(CellWorldPosition position) {
                terminalStates.add(getCellAt(position.getX(), position.getY()));

        }

        public CellWorldPosition getInitialState() {
                return initialState.position();
        }

        public MDPPerception<CellWorldPosition> execute(CellWorldPosition position,
                        String action, Randomizer r) {
                CellWorldPosition pos = moveProbabilisticallyFrom(position.getX(),
                                position.getY(), action, r);
                double reward = getCellAt(pos.getX(), pos.getY()).getReward();
                return new MDPPerception<CellWorldPosition>(pos, reward);
        }

        public List<String> getAllActions() {

                return Arrays.asList(new String[] { LEFT, RIGHT, UP, DOWN });
        }


    

    
     
}
